import * as Cesium from "cesium";
import { DrawType } from "../composables/ENUM";
import { SpacialTool } from "./SpacialTool";

export class DrawTool {
  viewer: Cesium.Viewer;
  ds: Cesium.CustomDataSource;
  isDrawing: boolean = false;
  drawType: DrawType = DrawType.POLYGON;
  tempEntity: Cesium.Entity | undefined;
  points: Cesium.Cartesian3[] = [];

  constructor(viewer: Cesium.Viewer, ds: Cesium.DataSource) {
    this.viewer = viewer;
    this.ds = ds;
  }

  startDraw(type: DrawType, id?: string) {
    this.drawType = type;
    this.isDrawing = true;
    this.initHandler();
  }

  private removeDraw() {
    const handler = this.viewer.screenSpaceEventHandler;
    handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
    handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE);
  }

  private handleDrawCircle() {
    const points = this.points;
    const handler = this.viewer.screenSpaceEventHandler;
    handler.setInputAction((evt: any) => {
      const position = evt.position;
      if (this.isDrawing) {
        const cartesian3 = SpacialTool.winPoint2Cartesian3(position, this.viewer);
        if (points.length === 0) {
          points.push(cartesian3!);
          this.tempEntity = this.ds.entities.add({
            position: points[0],
            ellipse: {
              semiMinorAxis: new Cesium.CallbackProperty(() => {
                const len = points.length;
                return Cesium.Cartesian3.distance(points[0], points[len - 1]);
              }, false),
              semiMajorAxis: new Cesium.CallbackProperty(() => {
                const len = points.length;
                return Cesium.Cartesian3.distance(points[0], points[len - 1]);
              }, false),
              height: 0,
              material: Cesium.Color.RED.withAlpha(0.5),
              outline: true,
              outlineColor: Cesium.Color.BLACK,
              outlineWidth: 2,
              rotation: 0,
              stRotation: 0,
              granularity: 0.005,
            },
          });
        } else {
          this.points = [];
          this.tempEntity = undefined;
          this.isDrawing = false;
          this.removeDraw();
        }
      }
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

    handler.setInputAction((evt: any) => {
      const position = evt.endPosition;
      if (this.isDrawing && this.points.length > 0) {
        const cartesian3 = SpacialTool.winPoint2Cartesian3(position, this.viewer);
        if (cartesian3) {
          this.points.push(cartesian3);
        }
      }
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
  }

  private handleDrawPoint() {
    const handler = this.viewer.screenSpaceEventHandler;
    let point = new Cesium.Cartesian3(0, 0, 0);

    this.tempEntity = this.ds.entities.add({
      position: new Cesium.CallbackPositionProperty(() => point, false),
      point: {
        color: Cesium.Color.RED,
        pixelSize: 10,
      },
    });

    handler.setInputAction((evt: any) => {
      if (this.isDrawing) {
        this.isDrawing = false;
        this.tempEntity = undefined;
        this.removeDraw();
      }
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

    handler.setInputAction((evt: any) => {
      if (this.isDrawing) {
        const cartesian3 = SpacialTool.winPoint2Cartesian3(
          evt.endPosition,
          this.viewer
        )!;
        point = cartesian3;
      }
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
  }

  private handleDrawRectangle() {
    const points = this.points;
    const handler = this.viewer.screenSpaceEventHandler;

    handler.setInputAction((evt: any) => {
      const position = evt.endPosition;
      if (this.isDrawing && this.points.length > 0) {
        const cartesian3 = SpacialTool.winPoint2Cartesian3(position, this.viewer);
        if (cartesian3) {
          this.points.push(cartesian3);
        }
      }
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

    handler.setInputAction((evt: any) => {
      const position = evt.position;
      if (this.isDrawing) {
        const cartesian3 = SpacialTool.winPoint2Cartesian3(position, this.viewer)!;
        if (this.points.length === 0) {
          points.push(cartesian3);
          this.tempEntity = this.ds.entities.add({
            rectangle: {
              coordinates: new Cesium.CallbackProperty(() => {
                const len = points.length;
                const p0 = points[0];
                const p1 = points[len - 1];
                return Cesium.Rectangle.fromCartesianArray([p0, p1]);
              }, false),
              material: Cesium.Color.RED.withAlpha(0.5),
              outline: true,
              height: 0,
              outlineColor: Cesium.Color.BLACK,
            },
          });
        } else {
          this.points = [];
          this.tempEntity = undefined;
          this.isDrawing = false;
          this.removeDraw();
        }
      }
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
  }

  private handleDrawPolygon() {
    const points = this.points;
    const handler = this.viewer.screenSpaceEventHandler;
    let tempPoint = new Cesium.Cartesian3(0, 0, 0);

    handler.setInputAction((evt: any) => {
      const position = evt.position;
      if (this.isDrawing) {
        const cartesian3 = SpacialTool.winPoint2Cartesian3(position, this.viewer);
        points.push(cartesian3!);
        if (!this.tempEntity) {
          this.tempEntity = this.ds.entities.add({
            polygon: {
              hierarchy: new Cesium.CallbackProperty(() => {
                return new Cesium.PolygonHierarchy([...points, tempPoint]);
              }, false),
              material: Cesium.Color.RED.withAlpha(0.5),
              outline: true,
              height: 0,
            },
          });
        }
      }
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

    handler.setInputAction((evt: any) => {
      const position = evt.endPosition;
      if (this.isDrawing && this.points.length > 0) {
        tempPoint = SpacialTool.winPoint2Cartesian3(position, this.viewer)!;
      }
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

    handler.setInputAction((evt: any) => {
      if (this.isDrawing) {
        this.points = [];
        this.tempEntity = undefined;
        this.removeDraw();
        this.isDrawing = false;
      }
    }, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
  }

  private handleDrawPolyline() {
    const handler = this.viewer.screenSpaceEventHandler;
    const points = this.points;
    let tempPoint: Cesium.Cartesian3;

    handler.setInputAction((evt: any) => {
      const position = evt.position;
      if (this.isDrawing) {
        const point = SpacialTool.winPoint2Cartesian3(position, this.viewer)!;
        points.push(point);
        if (!tempPoint) tempPoint = point;
        if (!this.tempEntity) {
          this.tempEntity = this.ds.entities.add({
            polyline: {
              positions: new Cesium.CallbackProperty(
                () => [...points, tempPoint],
                false
              ),
              material: Cesium.Color.RED.withAlpha(0.5),
              width: 3,
              //   clampToGround: true,
              //   arcType: Cesium.ArcType.NONE,
            },
          });
        }
      }
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

    handler.setInputAction((evt: any) => {
      const position = evt.endPosition;
      if (this.isDrawing && this.points.length > 0) {
        tempPoint = SpacialTool.winPoint2Cartesian3(position, this.viewer)!;
      }
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

    handler.setInputAction((evt: any) => {
      if (this.isDrawing) {
        this.points = [];
        this.isDrawing = false;
        this.removeDraw();
        this.tempEntity = undefined;
      }
    }, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
  }

  initHandler() {
    switch (this.drawType) {
      case DrawType.CIRCLE:
        this.handleDrawCircle();
        break;
      case DrawType.POLYLINE:
        this.handleDrawPolyline();
        break;
      case DrawType.POINT:
        this.handleDrawPoint();
        break;
      case DrawType.POLYGON:
        this.handleDrawPolygon();
        break;
      case DrawType.RECTANGLE:
        this.handleDrawRectangle();
        break;
    }
  }
}
